import { _decorator, Component, Node } from 'cc';
import { TileMapManager } from '../Tile/TileMapManager';
import { createUINode } from '../../utils';
import Levels, { ILevel } from '../../Levels';
import DataManager from '../../Runtime/DataManager';
import { TILE_HIGHT, TILE_WIDTH } from '../Tile/TileManager';
import EventManager from '../../Runtime/EventManager';
import { DIRECTION_ENUM, ENTITY_STATE_ENUM, ENTITY_TYPE_ENUM, EVENT_ENUM } from '../../Enums';
import { PlayerManager } from '../Player/PlayerManager';
import { WoodenSkeletonManager } from '../WoodenSkeleton/WoodenSkeletonManager';
import { DoorManager } from '../Door/DoorManager';
import { IronSkeletonManager } from '../IronSkeleton/IronSkeletonManager';
import { BurstManager } from '../Burst/BurstManager';
import { SpikesManager } from '../Spikes/SpikesManager';
import { SmokeManager } from '../Smoke/SmokeManager';
import FaderManager from '../../Runtime/FaderManager';
import ShakeManager from '../UI/ShakeManager';
const { ccclass, property } = _decorator;

@ccclass('BattleManager')
export class BattleManager extends Component {
    level: ILevel
    stage: Node
    private smokeLayer: Node

    onLoad() {
        DataManager.Instance.levelIndex = 1
        EventManager.Instance.on(EVENT_ENUM.NEXT_LEVEL, this.nextLevel, this)
        EventManager.Instance.on(EVENT_ENUM.PLAYER_MOVE_END, this.checkArrived, this)
        EventManager.Instance.on(EVENT_ENUM.SHOW_SMOKE, this.generateSmoke, this)
    }

    onDestroy() {
        EventManager.Instance.off(EVENT_ENUM.NEXT_LEVEL, this.nextLevel)
        EventManager.Instance.off(EVENT_ENUM.PLAYER_MOVE_END, this.checkArrived)
        EventManager.Instance.off(EVENT_ENUM.SHOW_SMOKE, this.generateSmoke)
    }

    start () {
        this.generateStage()
        this.initLevel()
    }

    async initLevel() {
      const level = Levels[`level${DataManager.Instance.levelIndex}`]
      if (level) {
        await FaderManager.Instance.fadeIn(1500)
        this.clearLevel()
        this.level = level

        DataManager.Instance.mapInfo = this.level.mapInfo
        DataManager.Instance.mapRowCount = this.level.mapInfo.length || 0
        DataManager.Instance.mapColumnCount = this.level.mapInfo[0].length || 0

        await Promise.all([
            this.generateTileMap(),
            this.generateBursts(),
            this.generateSpikes(),
            this.generateEnemies(),
            this.generateSmokeLayer(),
            this.generateDoor(),
            this.generatePlayer(),
        ])
        await FaderManager.Instance.fadeOut()
      }
    }
    async generateEnemies() {
        const promise = []
        for (let i = 0; i < this.level.enemies.length; i++) {
            const enemy = this.level.enemies[i]
            const node = createUINode()
            node.setParent(this.stage)
            const enemyManager = enemy.type === ENTITY_TYPE_ENUM.SKELETON_WOODEN ? WoodenSkeletonManager: IronSkeletonManager
            const manager = node.addComponent(enemyManager)
            promise.push(manager.init(enemy))
            DataManager.Instance.enemies.push(manager)
        }
        await Promise.all(promise)
    }
    async generatePlayer() {
        const player = createUINode()
        player.setParent(this.stage)
        const playerManager = player.addComponent(PlayerManager)
        await playerManager.init(this.level.player)
        DataManager.Instance.player = playerManager
        EventManager.Instance.emit(EVENT_ENUM.PLAYER_BORN, true)
    }

    async generateDoor() {
        const door = createUINode()
        door.setParent(this.stage)
        const doorManager = door.addComponent(DoorManager)
        await doorManager.init(this.level.door)
        DataManager.Instance.door = doorManager
    }

    async generateBursts() {
        const promise = []
        for (let i = 0; i < this.level.bursts.length; i++) {
            const burst = this.level.enemies[i]
            const node = createUINode()
            node.setParent(this.stage)
            const manager = node.addComponent(BurstManager)
            promise.push(manager.init(burst))
            DataManager.Instance.bursts.push(manager)
        }
        await Promise.all(promise)
    }

    async generateSpikes() {
        const promise = []
        for (let i = 0; i < this.level.spikes.length; i++) {
            const spikes = this.level.spikes[i]
            const node = createUINode()
            node.setParent(this.stage)
            const manager = node.addComponent(SpikesManager)
            promise.push(manager.init(spikes))
            DataManager.Instance.spikes.push(manager)
        }
        await Promise.all(promise)
    }

    nextLevel() {
        DataManager.Instance.levelIndex++
        this.initLevel()
    }

    clearLevel() {
        this.stage.destroyAllChildren()
        DataManager.Instance.reset()
    }

    generateStage() {
        this.stage = createUINode()
        this.stage.setParent(this.node)
        this.stage.addComponent(ShakeManager)
    }

    async generateTileMap() {
        const tileMap = createUINode()
        tileMap.setParent(this.stage)
        const tileMapManager = tileMap.addComponent(TileMapManager)
        await tileMapManager.init()

        this.adaptPos()
    }

    adaptPos() {
        const{ mapRowCount, mapColumnCount } = DataManager.Instance
        const disX = (TILE_WIDTH * mapColumnCount) / 2
        const disY = (TILE_HIGHT * mapRowCount) / 2 + 80
        this.stage.getComponent(ShakeManager).stop()
        this.stage.setPosition(-disX, disY)
    }

    checkArrived() {
        const {x:playerX, y:playerY} = DataManager.Instance.player
        const {x:doorX, y:doorY, state:doorState} = DataManager.Instance.door
        if (playerX === doorX && playerY === doorY && doorState === ENTITY_STATE_ENUM.DEATH) {
            EventManager.Instance.emit(EVENT_ENUM.NEXT_LEVEL)
        }
    }

    async generateSmoke(x:number, y:number, direction: DIRECTION_ENUM) {
        const item = DataManager.Instance.smokes.find(smoke => smoke.state === ENTITY_STATE_ENUM.DEATH)
        if (item) {
            console.log('smoke pool')
            item.x = x
            item.y = y
            item.direction = direction
            item.state = ENTITY_STATE_ENUM.IDLE
            item.node.setPosition(x * TILE_WIDTH - TILE_WIDTH * 1.5, -y * TILE_HIGHT + TILE_HIGHT * 1.5)
        } else {
            const smoke = createUINode()
            smoke.setParent(this.smokeLayer)
            const smokeManager = smoke.addComponent(SmokeManager)
            await smokeManager.init({
                x,
                y,
                direction,
                state: ENTITY_STATE_ENUM.IDLE,
                type: ENTITY_TYPE_ENUM.SMOKE,
            })
            DataManager.Instance.smokes.push(smokeManager)
        }
    }

    async generateSmokeLayer() {
        this.smokeLayer = createUINode()
        this.smokeLayer.setParent(this.stage)
    }
}
